#include "./move_motor.h"
#include "../xlib/gpio.h"
#include "../xlib/pwm.h"
#include "../rtos/event_flags.h"
#include "../pub_driver/step_motor_drv.h"
#include "./sensor.h"
#include "../utils/log.h"

namespace pearl_driver {

MoveMotor moveMotor;

namespace move_motor_impl {

using namespace stm32f1;
using namespace rtos::lazy;
using namespace pub_driver;

using PULS = gpio::PA<0>;
using DIR = gpio::PA<5>;
using ENABLE = gpio::PA<7>;
using ALM = gpio::PA<6>;
using PWM = pwm::Timer5PWM<1>;

struct DrvCfg : public SimpleDefaultCfg {

    using PULS_PIN = PULS;

    static void set_dir(SimpleDir dir) {
        switch (dir) {
            case SimpleDir::ENTER:
                DIR::clr();
            break;

            case SimpleDir::EXIT:
                DIR::set();
            break;

            case SimpleDir::DIRECT:
                DIR::set();
            break;
        }
    }

    static void set_pwm_freq(uint32_t freq) {
        PWM::set_freq(freq);
    }

    static void enable_pwm(uint32_t freq) {
        PULS::set_mode<gpio::Mode::OUT_50MHz_AF_PP>();
        PWM::start_with_irq(freq);
    }

    static void enable_pwm_not_irq(uint32_t freq) {
        PULS::set_mode<gpio::Mode::OUT_50MHz_AF_PP>();
        PWM::start_with_no_irq(freq);
    }

    static void disable_pwm() {
        PULS::set_mode<gpio::Mode::OUT_50MHz_PP>();
        PWM::stop();
    }

    static bool is_origin() {
        return sensor.is_move_origin();
    }

    static bool is_alm() {
        return !ALM::read();
    }

    static constexpr uint32_t BASE_FREQ = 400;
    static constexpr uint32_t ACC = 2;
    static constexpr uint32_t VAR_RANGE = 4000;

    static constexpr uint8_t EC_ALM = ec::pearl::MOVE_MOTOR_ALM;
    static constexpr uint8_t EC_TIMEOUT = ec::pearl::MOVE_MOTOR_TIMEOUT;

    static constexpr uint32_t SLOW_DELAY_MS = 3;

    static constexpr uint32_t RESET_PULS_COUNT = 800;
    static constexpr uint32_t RESET_TIMEOUT = 30 * 1000;
    static constexpr uint32_t RESET_FREQ = 1000;
};

static StepMotorDrv<DrvCfg> drv;

}

using namespace move_motor_impl;

void MoveMotor::init() {

    drv.init();

    rcc::enable<
        PULS, DIR, ENABLE, ALM, PWM
    >();

    gpio::config<
        gpio::Cfg<PULS, gpio::Mode::OUT_50MHz_PP>,
        gpio::Cfg<DIR, gpio::Mode::OUT_50MHz_PP>,
        gpio::Cfg<ENABLE, gpio::Mode::OUT_50MHz_PP>,
        gpio::Cfg<ALM, gpio::Mode::INPUT_FLOAT>
    >();
    ENABLE::clr();
    PWM::stop();
    NVIC_EnableIRQ(TIM5_IRQn);
}

Error MoveMotor::run(uint32_t position) {
    return drv.run_target(position);
}

bool MoveMotor::is_alm() const {
    return DrvCfg::is_alm();
}

}

using namespace pearl_driver;

extern "C"
void TIM5_IRQHandler() {
    if (PWM::is_uie()) {
        PWM::clear_flag();
        drv.on_puls();
    }
}
